package net.hust.atom.geotag;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import android.content.Context;
import android.content.Intent;
import android.location.Address;
import android.location.Criteria;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;

/**
 * Google Map
 * 
 * @author happy2v
 *
 */
public class MnMapActivity extends MapActivity {

	private final int MENU_ACCURACY_NO_REQUIREMENT	= 20;
	private final int MENU_ACCURACY_FINE			= 21;
	private final int MENU_ACCURACY_COARSE			= 22;
	
	private final int GEO_ADDRESS_MAX_COUNT 		= 100;
	
	private Context mContext;
	
	private TextWatcher watcher;
	private EditText editText;
	private MapView mapView;
	private LinearLayout loadingLayout;
	private ListView searchList;
	
	private MapController mapController;
	
	private LocationManager locationManager;
	private LocationListener myLocationListener;
	private Location currentLocation;
	private Criteria criteria;
	private Geocoder geocoder;
	private Locale mLocale;
	private List<Address> addresses;
	private MnMapSearchAdapter serachAdapter;
	
	private MnMapSearchTask searchTask;
	private boolean mSearching = false;
	private boolean goPosition = true;
	private boolean mapPositionSearch = true;
	
	private String ANDROID_SETTING_TAG = "com.android.settings";
	
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_geotag);
        
        mContext = this;
        
        locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
        mLocale = Locale.getDefault();
        geocoder = new Geocoder(this, mLocale);
        
        editText = (EditText)findViewById(R.id.geo_search);
        ImageView myLocation = (ImageView)findViewById(R.id.geo_my_position);
        myLocation.setImageResource(R.drawable.ic_menu_mylocation);
        ImageView geoSearch = (ImageView)findViewById(R.id.geo_search_button);
        Button sendBtn = (Button)findViewById(R.id.geo_send_btn);
        Button cancelBtn = (Button)findViewById(R.id.geo_cancel_btn);
        
        mapView = (MapView)findViewById(R.id.geo_map_view);
        
        loadingLayout = (LinearLayout)findViewById(R.id.lLayout_geo_loading);
        searchList = (ListView)findViewById(R.id.geo_search_list);
        
        // 설정 순서가 중요함.
        checkGPS();
        setMapView(false, false, false, true);
        setController(18);
        setLocationListener();
        setCriteria();
        setProvider();
        setTextWatcher();
	}	
	
	// GPS상태 확인후 설정 변경
	private void checkGPS(){
		if(!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
//			toggleGpsState();
			moveConfigGPS();
		}
	}
	
	// GPS 설정화면으로 이동
    private void moveConfigGPS() {
        Intent gpsOptionsIntent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
        startActivity(gpsOptionsIntent);
    }
	
    // 맵뷰를 설정한다.
    public void setMapView(boolean satellite, boolean streetview, boolean traffic, boolean zoomcontrols){
		setSatellite(satellite);
		setStreetView(streetview);
		setTraffic(traffic);
		setBuiltInZoomControls(zoomcontrols);
	}
	
	// 위성뷰 설정
	public void setSatellite(boolean satellite){
		mapView.setSatellite(satellite);
	}
	
	// 거리뷰 설정
	public void setStreetView(boolean streetview){
		mapView.setStreetView(streetview);
	}
	
	// 교통량 표시 설정
	public void setTraffic(boolean traffic){
		mapView.setTraffic(traffic);
	}
	
	// 줌 컨트롤 설정
	public void setBuiltInZoomControls(boolean zoomcontrols){
		mapView.setBuiltInZoomControls(zoomcontrols);
	}
	
	// 컨트롤러를 설정한다.(줌레벨 조절)
	private void setController(int zoom){
		mapController = mapView.getController();
		mapController.setZoom(zoom);
	}
	
	// 현재 위치로 이동한다.
	private void goPoint(){
		if(currentLocation != null && goPosition){
			GeoPoint point = new GeoPoint(
					(int)((currentLocation.getLatitude())*1E6), 
					(int)((currentLocation.getLongitude())*1E6));
			mapController.animateTo(point);
		}
	}
	
	// 프로바이더의 상태 및 위치 업데이트에 대한 처리
	private void setLocationListener(){
		myLocationListener = new LocationListener() {
			@Override
			public void onStatusChanged(String provider, int status, Bundle extras) {
				// 공급자 하드웨어 상태가 바뀐 경우 애플리케이션을 업데이트한다.
			}
			@Override
			public void onProviderEnabled(String provider) {
				// 공급자가 활성화된 경우 애플리케이션을 업데이트한다.
			}
			@Override
			public void onProviderDisabled(String provider) {
				// 공급자가 비활성화 된 경우 애플리케이션을 업데이트 한다.
			}
			@Override
			public void onLocationChanged(Location location) {
				// 새로운 위치에 기반해 애플리케이션을 업데이트 한다.
				loadingLayout.setVisibility(View.GONE);
				currentLocation = location;
				goPoint();
			}
		};
	}
	
	// Criteria 기본 설정
	private void setCriteria(){
		criteria = new Criteria();
		criteria.setAccuracy(Criteria.ACCURACY_COARSE);
		criteria.setPowerRequirement(Criteria.POWER_LOW);
		criteria.setAltitudeRequired(false);
		criteria.setBearingRequired(false);
		criteria.setSpeedRequired(false);
		criteria.setCostAllowed(true);
	}
	
	// 프로바이더를 설정한다.
	public void setProvider(){
		// 하나 이상일 경우 정확도가 가장 높은 것 선택
		// 없으면, 전력 사용 -> 정확도 -> 방위, 속도, 고도를 반환할 수 있는 능력 순으로 기준 완화
		// 금전 비용 기준은 암묵적으로 완화되지 않음
		String bestProvider = locationManager.getBestProvider(criteria, true);
		
		locationManager.requestLocationUpdates(bestProvider, 0, 0, myLocationListener);
		
		currentLocation = locationManager.getLastKnownLocation(bestProvider);
		goPoint();
	}
	
	// 위치 설정 
	public void setLocale(Locale locale){
		mLocale = locale;
		geocoder = new Geocoder(this, locale);
	}
	
	public Locale getLocale(){
		return mLocale;
	}
	
	// 텍스트 변화에 따른 검색
	private void setTextWatcher(){
		watcher = new TextWatcher() {
			@Override
			public void onTextChanged(CharSequence s, int start, int before, int count) {}
			@Override
			public void beforeTextChanged(CharSequence s, int start, int count,	int after) {
				if(addresses == null){
					if(geocoder != null){
						double latitude;
						double longitude;
						
						if(currentLocation != null && !mapPositionSearch){
							latitude = currentLocation.getLatitude();
							longitude = currentLocation.getLongitude();
						}else{
							GeoPoint mapCenterPoint = mapView.getMapCenter();
							latitude = mapCenterPoint.getLatitudeE6()/1E6;
							longitude = mapCenterPoint.getLongitudeE6()/1E6;
						}
						
						try {
							addresses = geocoder.getFromLocation(latitude, longitude, GEO_ADDRESS_MAX_COUNT);
						} catch (IOException e) {
							Log.e("ATOM", e.toString());
						}
					}
				}
			}
			@Override
			public void afterTextChanged(Editable s) {
				if(mSearching){
					searchTask.cancel(true);
					searchTask.onCancelled();
				}
				searchTask = new MnMapSearchTask();
				searchTask.execute(s.toString());
				mSearching = true;
			}
		};
	}
	
	@Override
	protected void onResume() {
		super.onResume();
		editText.addTextChangedListener(watcher);
	}
	
	@Override
	protected void onPause() {
		super.onPause();
		editText.removeTextChangedListener(watcher);
	}
        
	@Override
	protected boolean isRouteDisplayed() {
		// 방향 표시 안함(false)
		return false;
	}
	
	public void onSearch(View view){
		if(geocoder != null){
			double latitude;
			double longitude;
			
			if(currentLocation != null && !mapPositionSearch){
				latitude = currentLocation.getLatitude();
				longitude = currentLocation.getLongitude();
			}else{
				GeoPoint mapCenterPoint = mapView.getMapCenter();
				latitude = mapCenterPoint.getLatitudeE6()/1E6;
				longitude = mapCenterPoint.getLongitudeE6()/1E6;
			}
			
			try {
				addresses = geocoder.getFromLocation(latitude, longitude, GEO_ADDRESS_MAX_COUNT);
			} catch (IOException e) {
				Log.e("ATOM", e.toString());
			}
		}
	}
	
	public void onPosition(View view){
		if(goPosition){
			((ImageView)view).setImageResource(R.drawable.ic_menu_block);
			goPosition = false;
		}else{
			((ImageView)view).setImageResource(R.drawable.ic_menu_mylocation);
			goPosition = true;
		}
	}

	public void onSend(View view){
		Intent intent = new Intent();
		setResult(RESULT_OK, intent);
		finish();
	}
	
	public void onCancel(View view){
		Intent intent = new Intent();
		setResult(RESULT_CANCELED, intent);
		finish();
	}
	
	// GPS 토글 버튼
	private void toggleGpsState() {
		Intent intent = new Intent();
		intent.setClassName(ANDROID_SETTING_TAG, ANDROID_SETTING_TAG + ".widget.SettingsAppWidgetProvider");
		intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
		intent.setData(Uri.parse(String.valueOf(3))); // ;)
		this.sendBroadcast(intent);
	}
	
	private class MnMapSearchTask extends AsyncTask<String, Address, List<Address>> {
		/*
		 * 작업을 시작하기전에 UI 업데이트.
		 */
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
		}
		
		/*
		 * 빽그라운드에서 작업.(메인작업)
		 */
		@Override
		protected List<Address> doInBackground(String... params) {
			// 작업도중 진행사항을 UI에 표현하기 위한 설정
			String inputStr = params[0];
			List<Address> addressList = new ArrayList<Address>();
			if(!inputStr.equals("")){
				if(addresses != null && addresses.size() != 0){
					for(Address address : addresses){
						if(address.getAdminArea() != null){
							if(address.getAdminArea().contains(inputStr)){
								addressList.add(address);
								continue;
							}
						}
						if(address.getCountryCode() != null){
							if(address.getCountryCode().contains(inputStr)){
								addressList.add(address);
								continue;
							}
						}
						if(address.getCountryName() != null){
							if(address.getCountryName().contains(inputStr)){
								addressList.add(address);
								continue;
							}
						}
						if(address.getFeatureName() != null){
							if(address.getFeatureName().contains(inputStr)){
								addressList.add(address);
								continue;
							}
						}
						if(address.getLocality() != null){
							if(address.getLocality().contains(inputStr)){
								addressList.add(address);
								continue;
							}
						}
						if(address.getPhone() != null){
							if(address.getPhone().contains(inputStr)){
								addressList.add(address);
								continue;
							}
						}
						if(address.getPostalCode() != null){
							if(address.getPostalCode().contains(inputStr)){
								addressList.add(address);
								continue;
							}							
						}
						if(address.getPremises() != null){
							if(address.getPremises().contains(inputStr)){
								addressList.add(address);
								continue;
							}
						}
						if(address.getSubAdminArea() != null){
							if(address.getSubAdminArea().contains(inputStr)){
								addressList.add(address);
								continue;
							}
						}
						if(address.getSubLocality() != null){
							if(address.getSubLocality().contains(inputStr)){
								addressList.add(address);
								continue;
							}
						}
						if(address.getSubThoroughfare() != null){
							if(address.getSubThoroughfare().contains(inputStr)){
								addressList.add(address);
								continue;
							}
						}
						if(address.getThoroughfare() != null){
							if(address.getThoroughfare().contains(inputStr)){
								addressList.add(address);
								continue;
							}
						}
					}
				}
			}
			return addressList;
		}
		
		/*
		 * doInBackground 에서 publishProgress 가 호출되면 그 데이터를 받아서 UI를 업데이트 시켜줌.
		 */
		@Override
		protected void onProgressUpdate(Address... values) {
			super.onProgressUpdate(values);
		}
		
		/*
		 * 결과 처리
		 */
		@Override
		protected void onPostExecute(List<Address> result) {
			super.onPostExecute(result);
			serachAdapter = new MnMapSearchAdapter(mContext, result);
			searchList.setAdapter(serachAdapter);
		}
		
		/*
		 * 빽그라운드 작업이 취소 되었을때 해야될 작업.
		 */
		@Override
		protected void onCancelled() {
			super.onCancelled();
		}
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);
		menu.add(0, MENU_ACCURACY_NO_REQUIREMENT, 10, "no_requirment").setIcon(R.drawable.icon);
		menu.add(0, MENU_ACCURACY_FINE, 20, "accuracy_fine").setIcon(R.drawable.icon);
		menu.add(0, MENU_ACCURACY_COARSE, 30, "accuracy_coarse").setIcon(R.drawable.icon);
		return true;
	}
	
	@Override
	public boolean onMenuItemSelected(int featureId, MenuItem item) {
		switch(item.getItemId()){
			case MENU_ACCURACY_NO_REQUIREMENT:
				if(criteria != null){
					criteria.setAccuracy(Criteria.NO_REQUIREMENT);
					setProvider();
				}
				break;
			case MENU_ACCURACY_FINE:
				if(criteria != null){
					criteria.setAccuracy(Criteria.ACCURACY_FINE);
					setProvider();
				}
				break;
			case MENU_ACCURACY_COARSE:
				if(criteria != null){
					criteria.setAccuracy(Criteria.ACCURACY_COARSE);
					setProvider();
				}
				break;
			default:
		}
		return super.onMenuItemSelected(featureId, item);
	}
}